﻿namespace AddressBook.Business.Contact
{
	#region "Using Statements"
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;

	using Csla;
	using Csla.Data;
	using Csla.Validation;

	using Business.Event;
	#endregion

	[Serializable]
	public class Family : BusinessBase<Family>
	{
		#region "Business Methods"

		#region "Private Static Member Variables"

		private static PropertyInfo<Int32> IdProperty = RegisterProperty<Int32>(f => f.Id);
		private static PropertyInfo<String> NameProperty = RegisterProperty<String>(f => f.Name);
		private static PropertyInfo<SmartDate> ModifiedDateProperty = RegisterProperty<SmartDate>(f => f.ModifiedDate);
		private static PropertyInfo<FamilyAddress> FamilyAddressProperty = RegisterProperty<FamilyAddress>(f => f.Address);
		private static PropertyInfo<FamilyMembers> FamilyMembersProperty = RegisterProperty<FamilyMembers>(f => f.Members);

		#endregion

		#region "Public Properties"

		public Int32 Id
		{
			get { return GetProperty(IdProperty); }
			internal set { LoadProperty(IdProperty, value); }
		}

		public String Name
		{
			get { return GetProperty(NameProperty); }
			set { SetProperty(NameProperty, value); }
		}

		public FamilyAddress Address
		{
			get
			{
				if (!(FieldManager.FieldExists(FamilyAddressProperty)))
				{
					LoadProperty(FamilyAddressProperty, FamilyAddress.NewAddress());
				}

				return GetProperty(FamilyAddressProperty);
			}
		}

		public FamilyMembers Members
		{
			get
			{
				if (!(FieldManager.FieldExists(FamilyMembersProperty)))
				{
					LoadProperty(FamilyMembersProperty, FamilyMembers.NewFamilyMembers());
				}

				return GetProperty(FamilyMembersProperty);
			}
		}

		public string ModifiedDate
		{
      get { return GetProperty(ModifiedDateProperty).ToString(); }
		}

		#endregion

		#endregion

		#region "Business Rules"



		#endregion

		#region "Factory Methods"

    public static Family NewFamily()
    {
      return DataPortal.Create<Family>();
    }

		public static Family NewFamily(String name, String address1, String address2, String city, Int32 stateProvinceId, String postalCode)
    {
      return DataPortal.Create<Family>(new FamilyAddressCriteria(name, address1, address2, city, stateProvinceId, postalCode));
    }

		public static Family GetFamily(Int32 id)
    {
      return DataPortal.Fetch<Family>(new SingleCriteria<Family, Int32>(id));
    }

		public static void DeleteFamily(Int32 id)
    {
      DataPortal.Delete(new SingleCriteria<Family, Int32>(id));
    }

		private Family()
    { /* require use of factory methods */ }

		#endregion

		#region "Data Access"

		#region "Internal FamilyAddressCriteria Class"

		internal class FamilyAddressCriteria
		{
      public string Name;
			public String Address1;
			public String Address2;
			public String City;
			public Int32 StateProvinceId;
			public String PostalCode;

			public FamilyAddressCriteria(String name, String address1, String address2, String city, Int32 stateProvinceId, String postalCode)
			{
        this.Name = name;
				this.Address1 = address1;
				this.Address2 = address2;
				this.City = city;
				this.StateProvinceId = stateProvinceId;
				this.PostalCode = postalCode;
			}
		}

		#endregion

    [RunLocal()]
    protected override void DataPortal_Create()
    {
      using (BypassPropertyChecks)
      {
        ValidationRules.CheckRules();
      }
    }

		[RunLocal()]
		private void DataPortal_Create(FamilyAddressCriteria criteria)
		{
			using (BypassPropertyChecks)
			{
        this.Name = criteria.Name;

        Address.Address1 = criteria.Address1;
        Address.Address2 = criteria.Address2;
        Address.City = criteria.City;
        Address.StateProvinceId = criteria.StateProvinceId;
        Address.PostalCode = criteria.PostalCode;
			}
		}

    private void DataPortal_Fetch(SingleCriteria<Family, Int32> criteria)
    {
      using (var ctx = ContextManager<AddressBook.Business.Model.AddressBookDataContext>.GetManager(AddressBook.Business.Model.Database.AddressBook))
      {
        // get contact data
        AddressBook.Business.Model.Family family = ctx.DataContext.Families.First(f => f.Id == criteria.Value);

        using (BypassPropertyChecks)
        {
          this.Id = family.Id;
          this.Name = family.Name;

          LoadProperty(ModifiedDateProperty, family.ModifiedDate);

          // get child data
          LoadProperty(FamilyMembersProperty,
                       FamilyMembers.GetFamilyMembers(
                        family.FamilyMembers.OrderBy(fm => fm.Contact.FirstName).ToArray()));

          LoadProperty(FamilyAddressProperty,
                       FamilyAddress.GetAddress(family.Address));
        }
      }
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_Insert()
    {
      using (var ctx = ContextManager<AddressBook.Business.Model.AddressBookDataContext>.GetManager(AddressBook.Business.Model.Database.AddressBook))
      {
        // insert contact data
        using (BypassPropertyChecks)
        {
          AddressBook.Business.Model.Family family = new AddressBook.Business.Model.Family();

          this.Address.AddressId = Person.Address.AddAddress(this.Address.Address1,
                                                             this.Address.Address2,
                                                             this.Address.City,
                                                             this.Address.StateProvinceId,
                                                             this.Address.PostalCode);

          family.Name = this.Name;
          family.AddressId = this.Address.AddressId;
          family.ModifiedDate = System.DateTime.Now;

          ctx.DataContext.Families.InsertOnSubmit(family);
          ctx.DataContext.SubmitChanges();

          this.Id = family.Id;
        }

        // update child objects
        FieldManager.UpdateChildren(this);
      }
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_Update()
    {
      using (var ctx = ContextManager<AddressBook.Business.Model.AddressBookDataContext>.GetManager(AddressBook.Business.Model.Database.AddressBook))
      {
        AddressBook.Business.Model.Family family = ctx.DataContext.Families.First(f => f.Id == this.Id);

        // update family data
        using (BypassPropertyChecks)
        {
          family.Name = this.Name;
          family.ModifiedDate = System.DateTime.Now;

          ctx.DataContext.SubmitChanges();
        }

        // update child objects
        FieldManager.UpdateChildren(this);
      }
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_DeleteSelf()
    {
      DataPortal_Delete(new SingleCriteria<Family, Int32>(this.Id));
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    private void DataPortal_Delete(SingleCriteria<Family, Int32> criteria)
    {
      using (var ctx = ContextManager<AddressBook.Business.Model.AddressBookDataContext>.GetManager(AddressBook.Business.Model.Database.AddressBook))
      {
        // get family data
        AddressBook.Business.Model.Family family = ctx.DataContext.Families.First(f => f.Id == criteria.Value);

				foreach (AddressBook.Business.Model.Guest guest in ctx.DataContext.Guests.Where(g => g.GuestId == family.Id && g.GuestTypeId == (int)GuestType.Family))
				{
					ctx.DataContext.Guests.DeleteOnSubmit(guest);
				}

				foreach (AddressBook.Business.Model.FamilyMember member in ctx.DataContext.FamilyMembers.Where(m => m.FamilyId == family.Id))
				{
					ctx.DataContext.FamilyMembers.DeleteOnSubmit(member);
				}

				ctx.DataContext.SubmitChanges();

        // delete family data
        ctx.DataContext.Families.DeleteOnSubmit(family);
        ctx.DataContext.SubmitChanges();

        // reset child list field
        LoadProperty(FamilyAddressProperty, FamilyAddress.NewAddress());
        LoadProperty(FamilyMembersProperty, FamilyMembers.NewFamilyMembers());
      }
    }

		#endregion
	}
}
